// ============================== h2socket.h ==================================
//
#pragma once

#include <windows.h>
#include <winsock.h>
#include "h2base.h"
#include "h2log.h"

#define H2SOCKET_SEND_BUFF_SIZE     65535       // Send buffer max length
#define H2SOCKET_RECV_BUFF_SIZE     65535       // Recv buffer max length
#define H2SOCKET_RECV_BLOCK_SIZE    10240       // Recv block size



class packet
{
public:
    int             length;
    STRING::ansi    data;

public:
    packet()                                    {}
    ~packet()                                   {}

    virtual void    reset()                     {}
    virtual bool    done()                      { return true; }
    virtual void    set_done()                  {}
    virtual bool    with_host_info()            { return false; }

    virtual bool    encode()                    { return true; }
    virtual int     decode(char* buf, int len)  { return 0; }

public:
    inline void     set_property(const wchar_t* pp, int i)         { _properties[_properties.create(pp)].set(i); }  
    inline void     set_property(const wchar_t* pp, const char* s) { _properties[_properties.create(pp)].set(s); }
    inline int      int_property(const wchar_t* pp)                { return _properties[pp].integer(); }
    inline const char*  utf8_property(const wchar_t* pp)           { return _properties[pp].utf8(); }
    bool            has_property(const wchar_t* s)                 { return _properties.find(s) >= 0; }
    void            remove_property(const wchar_t* s)              { _properties.remove(s); }
    void            clear_property()                               { _properties.release(); }
    bool            is_int(const wchar_t* pp)                      { return _properties[pp].is_int(); }
    bool            is_string(const wchar_t* pp)                   { return _properties[pp].is_string(); }

    STRING::ansi    to_hex(bool multilines = false)
    {
        if(length <= 0) return "";

        STRING::ansi str(0);
        STRING::ansi tail(0);
        unsigned char *p = (unsigned char *)*data;
        int count = 0, line = 0;
        for(int i = 0; i < length; ++i, ++p)
        {
            char buf[16];

            if(count == 0)
            {
                sprintf_s(buf, 16, "%04X   ", (short)line);
                buf[6] = ' '; buf[7] = 0;
                STRING::cat(str, buf);
            }

            sprintf_s(buf, 14, "%02X ", *p);
            buf[2] = ' '; buf[3] = 0;
            STRING::cat(str, buf);

            if(!multilines) continue;

            tail.resize(count + 1);
            if(*p < 0x20) tail[count++] = '.'; else tail[count++] = *p;

            if(count >= 16 || i == length - 1)
            {
                for(int j = count; j < 16; ++j) STRING::cat(str, "   ");
                STRING::cat(str, " ");
                STRING::cat(str, *tail);
                STRING::cat(str, "\r\n");
                tail.resize(0);
                count = 0;
                line += 16;
            }
        }

        return str;
    }

    int hex_to_bin(STRING::ansi& hex)
    {
        STRING::ansi content = hex;
        STRING::ansi byte;

        int len = 0;
        while(content.length() > 0)
        {
            byte = STRING::split(content, "");
            if(byte.length() > 0)
            {
                hex.resize(len + 1);
                hex[len++] = (unsigned char)strtol(*byte, NULL, 16);
            }
        }

        return len;
    }

private:
    map_vector<var>   _properties;
};



// h2socket class
class h2socket
{
public:
	h2socket();
	~h2socket();

    // Winsock Control
	static int		init_winsock();						// call WSAStartup()
	static void	    release_winsock();					// call WSACleanup()
	
    // Socket Setup
	int		        set_buffer_size(int send_buffer_size, int recv_buffer_size);	// Allocate memory for send&recv buffer
	void	        set_sync_mode(bool bAsync = true);  // Set synchronize mode: true - Asynchronize, false - Block
	bool		    attach_socket(SOCKET sock);  // Attach existing socket handle as connect socket

    // Connection Setup
	void	        set_serv_host(const char *pszhost); // Set connect IP address
    void	        set_serv_port(int port) { _port = port; }   // Set connect port
    unsigned long   get_serv_ip()   { return _ip; }     // Get connect IP address
    int             get_serv_port() { return _port; }   // Get connect port
	bool		    connect();							    // Connect socket
	bool		    disconnect();					    // Close connect socket
	bool            connection_ready(int ms_waittime = 0);	// socket is connected
	bool            recv_ready(int ms_waittime = 0);	    // data arrived, or wait for it

    // Send & Recv
	int		        send(const char* pstrSend, int iSize);	// Send data to connect socket
	int		        send(packet* msg);                      // Send data to connect socket
    int             send_length()       { return _send_buff.length(); }
	int		        flush_buffer();                         // Flush send buffer to socket
	int             recv();                                 // Recv data from connect socket
	bool		    recv(packet* msg, int wait_ms = 0);	    // Recv data from connect socket
    int             recv_length()       { return _recv_buff.length(); }
    char*           recv_buffer()       { return *_recv_buff; }
    char*           recv_pop(int len)   { char* buff = *_recv_buff; _recv_buff.pop_front(len); return buff; }

private:

    data_vector<char>   _recv_buff;                     // recv buffer
	data_vector<char>   _send_buff;                     // send buffer

	SOCKET		        _socket;                        // socket handle
	int			        _port;                          // server port
	unsigned long       _ip;                            // server IP
};


#define HTTP_STATE_TITLE       4
#define HTTP_STATE_HEADER      3
#define HTTP_STATE_CONTENT     2
#define HTTP_STATE_CHUNK       1
#define HTTP_STATE_COMPLETE    0

class http_packet : public packet
{
public:
    map_vector<var>     cookie;
    map_vector<var>     post;

public:
        http_packet() : packet()            { reset(); }
    ~http_packet()                      {}

    virtual void        reset()
    {
        clear_property();
        set_property(L"Method", "GET");
        set_property(L"Version", "HTTP/1.1");
        set_property(L"Accept-Language", "zh-cn");
        set_property(L"Accept", "*/*");
        set_property(L"Accept-Encoding", "gzip, deflate");
        set_property(L"User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)");
        set_property(L"Connection", "Keep-Alive");
        _state = HTTP_STATE_TITLE;
        data.resize(0);
    }
    virtual bool        done()                          { return _state == HTTP_STATE_COMPLETE; }
    virtual void        set_done()                      { _state = HTTP_STATE_COMPLETE; }
    virtual bool        with_host_info()                { return true; }

    virtual bool        encode();
    virtual int         decode(char* buf, int len);

public:
    void                clear_cookie()                  { cookie.release(); }
    bool                set_cookie(const char* cookiestr);
    const char*         get_cookie(const wchar_t* name) { return cookie[name].utf8(); }
    bool                update_cookies();

private:
    int                _state;
};




class uap_packet : public packet
{
public:
    uap_packet() : packet()                             { reset(); }
    ~uap_packet()                                       {}

    virtual void        reset()                         { _state = false; data.resize(0); length = 0; clear_property(); }
    virtual bool        done()                          { return _state; }
    virtual void        set_done()                      { _state = true; }

    virtual bool        encode();
    virtual int         decode(char* buf, int len);

private:
    bool                _state;
};




class ussd_packet : public packet
{
public:
    ussd_packet() : packet()                            { reset(); }
    ~ussd_packet()                                      {}

    virtual void        reset()                         { _state = false; data.resize(0); length = 0; clear_property(); }
    virtual bool        done()                          { return _state; }
    virtual void        set_done()                      { _state = true; }

    virtual bool        encode();
    virtual int         decode(char* buf, int len);

private:
    bool                _state;
};




#define ESME_BNDRCV             0x00000001
#define ESME_BNDRCV_RESP        0x80000001
#define ESME_BNDTRN             0x00000002
#define ESME_BNDTRN_RESP        0x80000002
#define ESME_SUB_SM             0x00000004
#define ESME_SUB_SM_RESP        0x80000004
#define SMSC_DELIVER_SM         0x00000005
#define SMSC_DELIVER_SM_RESP    0x80000005
#define ESME_UBD                0x00000006
#define ESME_UBD_RESP           0x80000006
#define ESME_QRYLINK            0x00000015
#define ESME_QRYLINK_RESP       0x80000015
#define ESME_NACK	            0x80000000


class smpp_packet : public packet
{
public:
    smpp_packet() : packet()                            { reset(); }
    ~smpp_packet()                                      {}

    virtual void        reset()                         { _state = false; data.resize(0); length = 0; clear_property(); }
    virtual bool        done()                          { return _state; }
    virtual void        set_done()                      { _state = true; }

    virtual bool        encode();
    virtual int         decode(char* buf, int len);

private:
    bool                _state;
};

